classdef NanoObj < handle
    %   NanoObj includes information about an atomistics structure.
    %   Detailes explanation goes here
    
   
    properties (GetAccess = public, SetAccess = private)
        Atoms = [];
        specie = {};
    end
    
    properties (GetAccess = private, SetAccess = private)
       index = [];
    end
    
    methods 
        
        function n_atoms = natoms(obj)
            %N_ATOMS gives the number of atoms.
            %Example: obj.n_atoms 
            n_atoms = length(obj.Atoms);
        end
        
        function obj = NanoObj(varargin)
            %NANOOBJ(ARGS) creates a new NanoObj object. There are three
            %ways to isntantiates it:
            %obj = NanoObj %create an empy object
            %obj = NanoObj([1,0,0],'O) %create an object with an atom
            %obj = NanoObj(obj2) %Copy a copy of the object2.
            obj.Atoms = containers.Map('KeyType','uint64', 'ValueType','any');
            obj.index = 0;
            if nargin == 2 %Atom is specified
               obj.add_atom(varargin{1},varargin{2});
            end
            if (nargin == 1)  
               if isa(varargin{1},'NanoObj')
                   
                  obj = NanoObj;
                  obji = varargin{1};
                  indexes = obji.Atoms.keys;
                  
                  for k = 1:numel(indexes)
                      Atom = obji.get_atom(indexes{k});
                      obj.add_atom(Atom);                          
                  end
                
                  
               end
            end
        end
    
      
        %---------------------------Interfaces
        function indexes = select(obj,varargin)   
            %SELECT(MODE,ARGS) selects atoms accroding to the criterium
            %MODE with arguments ARGS. MODE can be 
            %specie
            %  args: specie
            %  Example: indexes = obj.select("specie","Si")      
            indexes = eval(strcat('obj.select_',varargin{1},'(varargin{2})'));
        end
        
        function [positions] = get_positions(obj,varargin)
        %GET_POSITION(INDEX) gives the position of the atom "index"
        %Example: get_position(1)    
            
             if (nargin == 1) 
              indexes = obj.Atoms.keys;
             else
              indexes = varargin{1};   
             end
             
             natoms = numel(indexes);
             positions = zeros(natoms,3);
             
             for k = 1:numel(indexes)
              positions(k,:) = obj.Atoms(indexes(k)).Position;   
             end
           
        end
       
        function import(obj,varargin)
            %IMPORT(FILENAME) imports atoms from the file "filename" and add them to the current atoms. 
            %Example: obj.import("benzene.xyz")
            filename = varargin{1};
            filetype = filename(end-2:end);
            eval(strcat('import_',filetype,'(obj,varargin{:})'));
        end
        
        
        function export(obj,varargin)
          %EXPORT(FILENAME) write atoms to a file FILENAME.
            % The format is guessed by the estension. Currently, 
            %only "xyz" format is supported.
            %Example:  obj.plot('nanowire.xyz') 
           
            filename = varargin{1};
            filetype = filename(end-2:end);
            eval(strcat('export_',filetype,'(obj,varargin)'));
            
        end
        
        function plot(obj,varargin)
            %PLOT(TYPE) plots the current atoms to a GUI or file.
            %Currently,the only avaialbe tool is "vmd". 
            %NOTE: to use this molecular visualizer you must have the
            %Bioinformatics toolbox.
            %Example:  obj.plot("vmd")
         
            eval(strcat('h = plot_',varargin{1},'(obj,varargin)'));
        end
        
        
        %add_atom
        function output = add_atom(obj,varargin)
            %ADD_ATOM(POSITION,SPECIE) adds an atom to the structure.
            %Position must be a 1x3 vector. Specie must be a string.
            %Example: add_atom([1,0,0],'Si')
            
            %Update the specie list
            specie_index = -1;
            for k=1:numel(obj.specie)
                if (obj.specie{k} == varargin{2})
                    specie_index = k;
                end
            end
            if(specie_index == -1)
              obj.specie{end+1} = varargin{2};
              specie_index = numel(obj.specie);
            end
            %---------------
            
            obj.index = obj.index + 1;
            if (nargin == 2)
               obj.Atoms(obj.index) = varargin{1};
            else
               obj.Atoms(obj.index) = struct('Position',varargin{1},'Specie',specie_index);     
            end
            output =  obj.index; 
            
            
        end
        
        function rm_atom(obj,index)
            %RM_ATOM(INDEX) removes the atom "index"
            %Example: obj.rm_atom(index).
         
            obj.Atoms.remove(index);
        end
        
        function set_element(obj,index,varargin)
        %SET_ELEMENT(POSITION,SPECIE) assignes a new species to the existing atom
        %"index". 
        %Example: set_element(1,'Si')
             if (isKey(obj.Atoms,{index}))
                 Atom = struct('Position',obj.Atoms(index).Position,'Specie',varargin{1});
                 obj.Atoms(index) = Atom;
             else  
                 error(strcat('No Atoms with index: ',index)); 
             end
          
        end
        
        
         function set_position(obj,index,varargin)
         %SET_POSITION(INDEX,POSITION) assignes a new position POSITION to the existing atom
         %"index". 
         %Example: set_element(1,[0,0,3])
             if (isKey(obj.Atoms,{index}))
               Atom = struct('Position',varargin{1},'Specie',obj.Atoms(index).Kind);
                obj.Atoms(index) = Atom;  
             else  
                 error(strcat('No Atoms with index: ',index)); 
             end
          
         end
         
         function output = duplicate_atom(obj,index,varargin)
          %DUCPLICATE_ATOM(INDEX,TRANSLATE) adds an atom whose position is P+T where P is the 
          %position of the atom "index" and T is the "traslate"
          %Example: duplicate_atom(45,[0,1,0])
             if (isKey(obj.Atoms,{index}))
               Atom = obj.Atoms(index);
               output = obj.add_atom(Atom);
               if (nargin == 3)
                  obj.set_element(output,varargin{1});
               end
               if (nargin == 4)
                  obj.set_element(output,varargin{1});
                  obj.translate(output,varargin{2});
               end      
             else  
                 error(strcat('No Atoms with index: ',index)); 
             end
          
         end
       
        
       function set_atom(obj,index,varargin)
        %SET_ATOM(INDEX,POSITION,KIND) assignes a new position and specie to the existing atom
        %"index". 
        %Example: set_element(1,[0,0,3],'H')
            if (isKey(obj.Atoms,{index}))
               if (nargin == 3)
                   obj.Atoms(index) = varargin{1};
               else
                   Atom = struct('Position',varargin{1},'Specie',varargin{2});
                   obj.Atoms(index) = Atom;
               end
            else
               error(strcat('No Atoms with index: ',index)); 
            end
               
        end
        
        % Plus operator
        function obj = plus(obj1,obj2)
        %PLUS operator simple add two NanoObjects. 
        
            obj = obj1;
            indexes = obj2.Atoms.keys;
            for k = 1:numel(indexes)
              Atom = obj2.Atoms(indexes{k});  
              obj.add_atom(Atom); 
            end   
            A1  = obj1.Periodicity;
            A2  = obj2.Periodicity;
            obj.Periodicity = A1 + A2;
        end
        
    translate(obj,varargin);
    rotate(obj,direction,theta);   
    
    end  
    methods(Access = private)
        
      output = select_specie(obj,varargin);
      export_xyz(obj,varargin);
      import_xyz(obj,varargin);
      output = plot_vmd(obj,varargin);
       
        
    end
    
end

